package com.powerband.apiheartrate.heartrate

import android.app.Application
import com.powerband.apiheartrate.manager.FBKManagerControllerCallBack
import com.powerband.apiheartrate.manager.FBKManagerController
import com.powerband.apiheartrate.ble.fbkBleDevice.FBKBleDeviceStatus
import android.bluetooth.BluetoothGattCharacteristic
import com.powerband.apiheartrate.base.*
import com.powerband.apiheartrate.ble.fbkBleDevice.FBKBleDevice
import com.powerband.apiheartrate.ota.OtaError
import com.powerband.apiheartrate.ota.OtaStatus
import java.util.concurrent.ConcurrentHashMap


object FBKApiHeartRate : FBKApiBaseMethod() {
    @set:Synchronized
    @get:Synchronized
    private var apiBasicInfoCallBack: FBKBasicInfoCallBack? = null

    //    private var apiBleCallBack: FBKBleCallBack? = null
    @set:Synchronized
    @get:Synchronized
    private var apiBleScanCallBack: FBKScanResultCallBack? = null

    private var mapOfHeartRate: ConcurrentHashMap<String, FBKHearRateCallBack> = ConcurrentHashMap()

    private var mapOfBleCallback: ConcurrentHashMap<String, FBKBleCallBack> = ConcurrentHashMap()

    private var mManagerControllerCallBack: FBKManagerControllerCallBack =
        object : FBKManagerControllerCallBack {

            override fun onFinishDiscovery() {
                apiBleScanCallBack?.onFinishDiscovery()
            }

            override fun onDiscoveryDevice(result: FBKBleDevice) {
                apiBleScanCallBack?.onDiscoveryDevice(result)

            }

            override fun bleScanError(error: String?, managerController: FBKManagerController?) {
                apiBleScanCallBack?.onScanError(error, managerController)
            }

            override fun bleConnectError(error: String?, managerController: FBKManagerController?) {
                mapOfBleCallback.values.forEach {
                    it.bleConnectError(error, this@FBKApiHeartRate)
                }

            }

            override fun bleConnectStatus(
                connectStatus: FBKBleDeviceStatus?,
                managerController: FBKManagerController?
            ) {
                mapOfBleCallback.values.forEach {
                    it.bleConnectStatus(connectStatus, this@FBKApiHeartRate)
                }

            }

            override fun otaError(error: OtaError) {
                mapOfBleCallback.values.forEach {
                    it.bleOtaError(error)
                }

            }

            override fun otaLog(status: OtaStatus, progress: Float) {
                mapOfBleCallback.values.forEach {
                    it.bleOtaLog(status, progress)
                }

            }

            override fun bleConnectStatusLog(
                logString: String?,
                managerController: FBKManagerController?
            ) {
                mapOfBleCallback.values.forEach {
                    it.bleConnectStatusLog(logString, this@FBKApiHeartRate)
                }
            }

            override fun bleWriteDataResult(
                isSucceed: Boolean,
                managerController: FBKManagerController?
            ) {
            }

            override fun bleCommonResult(
                resultData: Any,
                managerController: FBKManagerController?
            ) {
            }

            override fun bleConnectUuids(
                charUuidArray: List<BluetoothGattCharacteristic>?,
                managerController: FBKManagerController?
            ) {

            }

            override fun bleConnectInfo(
                information: String?,
                managerController: FBKManagerController?
            ) {
                apiBasicInfoCallBack?.bleConnectInfo(information, this@FBKApiHeartRate)
            }

            override fun logOutBufferData(data: List<Byte>) {
                apiBasicInfoCallBack?.logOutBufferData(data)
            }

            override fun totalPacket(num: Int) {
                apiBasicInfoCallBack?.totalPacket(num)
            }



            override fun bleConnectResult(
                resultData: ByteArray,
                resultKey: FBKArmBandCmd,
                managerController: FBKManagerController?
            ) {
                when (resultKey) {
                    FBKArmBandCmd.Battery -> apiBasicInfoCallBack?.batteryPower(
                        resultData.first().toInt(),
                        this@FBKApiHeartRate
                    )

                    FBKArmBandCmd.Firmware -> apiBasicInfoCallBack?.firmwareVersion(
                        String(resultData),
                        this@FBKApiHeartRate
                    )

                    FBKArmBandCmd.Hardware -> apiBasicInfoCallBack?.hardwareVersion(
                        String(resultData),
                        this@FBKApiHeartRate
                    )

                    FBKArmBandCmd.ModelNum -> apiBasicInfoCallBack?.deviceModelString(
                        String(resultData),
                        this@FBKApiHeartRate
                    )

                    FBKArmBandCmd.Software -> apiBasicInfoCallBack?.softwareVersion(
                        String(resultData),
                        this@FBKApiHeartRate
                    )

                    FBKArmBandCmd.SystemId -> apiBasicInfoCallBack?.deviceSystemID(
                        resultData,
                        this@FBKApiHeartRate
                    )

                    FBKArmBandCmd.Manufacture -> apiBasicInfoCallBack?.deviceManufacturerName(
                        String(resultData),
                        this@FBKApiHeartRate
                    )

                    FBKArmBandCmd.Serial -> apiBasicInfoCallBack?.deviceSerialNumber(
                        resultData.drop(1).first().toString(),
                        this@FBKApiHeartRate
                    )

                    FBKArmBandCmd.StepFrequency -> {
                        val sub = resultData.drop(1)
                        var sep = 0
                        if (sub.size > 1) {
                            sep = (sub[1].toInt() and 0xFF).shl(8) or (sub[0].toInt() and 0xFF)
                        }

                        apiBasicInfoCallBack?.deviceStepFrequency(sep, this@FBKApiHeartRate)
                    }

                    FBKArmBandCmd.Pressure -> apiBasicInfoCallBack?.devicePressure(
                        resultData.drop(1).first().toInt(),
                        this@FBKApiHeartRate
                    )

                    FBKArmBandCmd.Threshold -> apiBasicInfoCallBack?.deviceThresholdChanged()
                    FBKArmBandCmd.SyncTime -> apiBasicInfoCallBack?.deviceTimeSynced()
                    FBKArmBandCmd.DisturbTime -> apiBasicInfoCallBack?.doNotDisturbTimeInfo(
                        true
                    )

                    FBKArmBandCmd.RealTimeOxygen -> apiBasicInfoCallBack?.deviceOxygen(
                        resultData.drop(1).first().toInt(),
                        this@FBKApiHeartRate
                    )

                    FBKArmBandCmd.PlayStatus -> apiBasicInfoCallBack?.armBandPlayStatusChange()
                    FBKArmBandCmd.Unbind -> apiBasicInfoCallBack?.armBandUnbind()
                    FBKArmBandCmd.BatteryStatus -> apiBasicInfoCallBack?.batteryStatus(
                        mapBatteryStatus(resultData)
                    )
                    FBKArmBandCmd.LightControl -> {
                        if (resultData.size < 2) {
                            apiBasicInfoCallBack?.lightStatusChanged(false)
                        } else {
                            apiBasicInfoCallBack?.lightStatusChanged(resultData[1].toInt() == 1)
                        }
                    }

                    FBKArmBandCmd.HeartRateLog -> {
                        logOutBufferData(resultData.toList())
                        if (resultData.size < 49) {
                            return
                        }

                        val rArray: List<Any> = listOf(
                            (resultData[1].toInt() and 0xFF).toUInt(),
                        (resultData[2].toInt() and 0xFF).toUInt(),
                            (resultData[3].toInt() and 0xFF).toUInt(),
                            (resultData[4].toInt() and 0xFF).toUInt(),
                            (resultData[5].toInt() and 0xFF).toUInt(),
                            (resultData[6].toInt() and 0xFF).toUInt(),
                            map32UInt(resultData.sliceArray(7..10)),
                            map32UInt(resultData.sliceArray(11..14)),
                            map32UInt(resultData.sliceArray(15..18)),
                            map32UInt(resultData.sliceArray(19..20)),
                            map32UInt(resultData.sliceArray(21..22)),
                            map32UInt(resultData.sliceArray(23..24)),
                            map32UInt(resultData.sliceArray(25..28)),
                            map32Int(resultData.sliceArray(29..32)),
                            map32Int(resultData.sliceArray(33..36)),
                            map32Int(resultData.sliceArray(37..40)),
                            map32Int(resultData.sliceArray(41..44)),
                            map32Int(resultData.sliceArray(45..48)),
                        )
                        apiBasicInfoCallBack?.heartRateLog(rArray)
                    }

                    FBKArmBandCmd.ExitAlgorithm -> apiBasicInfoCallBack?.exitedSleepAlgorithm()
                    FBKArmBandCmd.RateInitial -> apiBasicInfoCallBack?.heartRateInMeasuring()
                    FBKArmBandCmd.DeviceMode -> {
                        val state =
                            if (resultData[1].toInt() == 1) FBKDeviceMode.Sport else FBKDeviceMode.Daily
                        var ty = FBKModeType.Other
                        if (resultData.size > 2) {
                            ty = when (resultData[2].toInt()) {
                                0x01 -> FBKModeType.AutoIn
                                0x02 -> FBKModeType.AutoOut
                                else -> FBKModeType.Other
                            }
                        }
                        apiBasicInfoCallBack?.armBandModeChanged(state, ty)
                    }

                    FBKArmBandCmd.HeartRate -> {
                        mapOfHeartRate.values.forEach {
                            it.deviceHeartRate(
                                resultData.drop(1).first().toInt() and 0xFF,
                                this@FBKApiHeartRate
                            )
                        }
                    }

                    FBKArmBandCmd.AckMotion -> apiBasicInfoCallBack?.motionDataSeqUpdated(
                        (resultData.getOrNull(
                            0
                        )?.toInt() ?: 0) == 1
                    )

                    FBKArmBandCmd.CurrentTime -> {
                        val data = resultData.drop(1)
                        var time = 0
                        if (data.size == 4) {
                            time = (data[0].toInt() and 0xFF) or
                                    (data[1].toInt() and 0xFF).shl(8) or
                                    (data[2].toInt() and 0xFF).shl(16) or
                                    (data[3].toInt() and 0xFF).shl(24)
                        }
                        apiBasicInfoCallBack?.currentBandTime(time * 1000L)
                    }

                    FBKArmBandCmd.SportModeDetail -> {
                        val data = resultData.drop(1)
                        if (data.size < 11) {
                            return
                        }
                        apiBasicInfoCallBack?.sportModeDetailInfo(
                            mapOf<String, Int>(
                                "current" to (data[0].toInt() and 0xFF),
                                "type" to (data[1].toInt() and 0xFF),
                                "level" to (data[2].toInt() and 0xFF),
                                "daily" to (data[3].toInt() and 0xFF),
                                "low" to (data[4].toInt() and 0xFF),
                                "heavy" to (data[5].toInt() and 0xFF),
                                "rate" to (data[6].toInt() and 0xFF),
                                "sp1" to (data[7].toInt() and 0xFF),
                                "sp2" to (data[8].toInt() and 0xFF),
                                "sp3" to (data[9].toInt() and 0xFF),
                                "dl1" to (data[10].toInt() and 0xFF),
                                "dl2" to (data[11].toInt() and 0xFF),
                                "dl3" to (data[11].toInt() and 0xFF),

                                )
                        )
                    }

                    else -> {}
                }
            }

            override fun bleSummaryInfo(info: List<Map<String, Any>>, type: FBKSummaryType) {
                when (type) {
                    FBKSummaryType.HeartRate -> apiBasicInfoCallBack?.summaryHeartRateInfo(info)
                    FBKSummaryType.Step -> apiBasicInfoCallBack?.summaryStepsInfo(info)
                    FBKSummaryType.Pressure -> apiBasicInfoCallBack?.summaryPressureIndexInfo(info)
                    FBKSummaryType.Oxgen -> apiBasicInfoCallBack?.summaryBloodOxygenInfo(info)
                    FBKSummaryType.Sleep -> apiBasicInfoCallBack?.summarySleepInfo(info)
                    FBKSummaryType.Sport -> apiBasicInfoCallBack?.summaryExerciseInfo(info)
                    FBKSummaryType.Calorie -> apiBasicInfoCallBack?.summaryCalorieInfo(info)
                    FBKSummaryType.ModeSport, FBKSummaryType.ModeDaily -> apiBasicInfoCallBack?.motionDataInfo(
                        info, type == FBKSummaryType.ModeSport
                    )
                }
            }

        }

    fun map32Int(bytes: ByteArray): Int {
        var ret = 0;
        for (i in bytes.indices) {
            ret = ret or bytes[i].toInt().shr(8 * i)
        }
        return ret
    }

    fun map32UInt(bytes: ByteArray): UInt {
        var ret:UInt = 0u;
        for (i in bytes.indices) {
            ret = ret or bytes[i].toUByte().toUInt().shr(8 * i)
        }
        return ret
    }

    internal fun mapBatteryStatus(byte: ByteArray): BatteryStatus {
        if (byte.size < 2) {
            return BatteryStatus.Unkonwn
        }
        return when (byte[1].toInt()) {
            0 -> BatteryStatus.Default
            1 -> BatteryStatus.Charging
            2 -> BatteryStatus.Fullfilled
            else -> {
                BatteryStatus.Unkonwn
            }
        }
    }

    fun initConfig(context: Application): FBKApiHeartRate {
        mManagerController = FBKManagerController(context, mManagerControllerCallBack)
        return this
    }

    fun setBleCallBack(cls: String, callBack: FBKBleCallBack): FBKApiHeartRate {
        mapOfBleCallback[cls] = callBack
        return this
    }

    fun setBleScanCallBack(callBack: FBKScanResultCallBack): FBKApiHeartRate {
        apiBleScanCallBack = callBack
        return this
    }

    fun setBasicInfoCallBack(callBack: FBKBasicInfoCallBack?): FBKApiHeartRate {
        apiBasicInfoCallBack = callBack
        return this
    }

    fun setHeartRateCallBack(cls: String, callBack: FBKHearRateCallBack): FBKApiHeartRate {
        mapOfHeartRate[cls] = callBack
        return this
    }

    fun removeHeartRateCallBack(cls: String) {
        mapOfHeartRate.remove(cls)
    }

    fun removeBleCallBack(cls: String) {
        mapOfBleCallback.remove(cls)
    }
}